#ifndef SOCCERTEAM_H
#define SOCCERTEAM_H
//------------------------------------------------------------------------
//
//  Name:   State.h
//
//  Desc:   abstract base class to define an interface for a state
//
//  Author: Mat Buckland (fup@ai-junkie.com)
//
//------------------------------------------------------------------------

#include "OgreRefAppPrerequisites.h"
#include "OgreRefAppApplicationObject.h"
#include "OgreRefAppStateMachine.h"
#include "OgreRefAppSoccerTeamOwnedStates.h"
#include "OgreRefAppSupportSpotCalculator.h"

#include <vector>

namespace OgreRefApp
{

    class FieldPlayer;
    class Goal;
    class SoccerPitch;
    class PlayerBase;
    class GoalKeeper;


    class SoccerTeam
    {
    public:
        enum team_color {blue, red};

    private:

        //an instance of the state machine class
        StateMachine<SoccerTeam>*  m_pStateMachine;

        //the team must know its own color!
        team_color                m_Color;

        //pointers to the team members
        std::vector<PlayerBase*>  m_Players;

        //a pointer to the soccer pitch
        SoccerPitch*              m_pPitch;

        //pointers to the goals
        Goal*                     m_pOpponentsGoal;
        Goal*                     m_pHomeGoal;

        //a pointer to the opposing team
        SoccerTeam*               m_pOpponents;

        //pointers to 'key' players
        PlayerBase*               m_pControllingPlayer;
        PlayerBase*               m_pSupportingPlayer;
        PlayerBase*               m_pReceivingPlayer;
        PlayerBase*               m_pPlayerClosestToBall;

        //the squared distance the closest player is from the ball
        Real                     m_dDistSqToBallOfClosestPlayer;

        //players use this to determine strategic positions on the playing field
        SupportSpotCalculator*    m_pSupportSpotCalc;


        //creates all the players for this team
        void CreatePlayers();

        //called each frame. Sets m_pClosestPlayerToBall to point to the player
        //closest to the ball.
        void CalculateClosestPlayerToBall();


    public:

        SoccerTeam(Goal*        home_goal,
                   Goal*        opponents_goal,
                   SoccerPitch* pitch,
                   team_color   color);

        ~SoccerTeam();

        //the usual suspects
        //void        Render()const;
        void        update();

        //calling this changes the state of all field players to that of
        //ReturnToHomeRegion. Mainly used when a goal keeper has
        //possession
        void        ReturnAllFieldPlayersToHome();

        //returns true if player has a clean shot at the goal and sets ShotTarget
        //to a normalized vector pointing in the direction the shot should be
        //made. Else returns false and sets heading to a zero vector

        bool        CanShoot(Ogre::Vector3  BallPos,
                             Real     power,
                             Ogre::Vector3* ShotTarget = new Ogre::Vector3());

        //The best pass is considered to be the pass that cannot be intercepted
        //by an opponent and that is as far forward of the receiver as possible
        //If a pass is found, the receiver's address is returned in the
        //reference, 'receiver' and the position the pass will be made to is
        //returned in the  reference 'PassTarget'
        bool        FindPass(PlayerBase* passer,
                             PlayerBase*           receiver,
                             Ogre::Vector3              PassTarget,
                             Real                  power,
                             Real                  MinPassingDistance);

        //Three potential passes are calculated. One directly toward the receiver's
        //current position and two that are the tangents from the ball position
        //to the circle of radius 'range' from the receiver.
        //These passes are then tested to see if they can be intercepted by an
        //opponent and to make sure they terminate within the playing area. If
        //all the passes are invalidated the function returns false. Otherwise
        //the function returns the pass that takes the ball closest to the
        //opponent's goal area.
        bool        GetBestPassToReceiver(PlayerBase* passer,
                                          PlayerBase* receiver,
                                          Ogre::Vector3 PassTarget,
                                          Real power);

        //test if a pass from positions 'from' to 'target' kicked with force
        //'PassingForce'can be intercepted by an opposing player
        bool        isPassSafeFromOpponent(Ogre::Vector3    from,
                                           Ogre::Vector3   target,
                                           PlayerBase* receiver,
                                           PlayerBase* opp,
                                           Real      PassingForce);

        //tests a pass from position 'from' to position 'target' against each member
        //of the opposing team. Returns true if the pass can be made without
        //getting intercepted
        bool        isPassSafeFromAllOpponents(Ogre::Vector3 from,
                                               Ogre::Vector3 target,
                                               PlayerBase* receiver,
                                               Real     PassingForce);

        //returns true if there is an opponent within radius of position
        bool        isOpponentWithinRadius(Ogre::Vector3 pos, Real rad);

        //this tests to see if a pass is possible between the requester and
        //the controlling player. If it is possible a message is sent to the
        //controlling player to pass the ball asap.
        void        RequestPass(PlayerBase* requester);

        //calculates the best supporting position and finds the most appropriate
        //attacker to travel to the spot
        PlayerBase* DetermineBestSupportingAttacker();


        const std::vector<PlayerBase*>& Members()
        {
            return m_Players;
        }

        StateMachine<SoccerTeam>* GetFSM()
        {
            return m_pStateMachine;
        }

        Goal*           HomeGoal()
        {
            return m_pHomeGoal;
        }
        Goal*          OpponentsGoal()
        {
            return m_pOpponentsGoal;
        }

        SoccerPitch*    Pitch()
        {
            return m_pPitch;
        }

        SoccerTeam*     Opponents()
        {
            return m_pOpponents;
        }
        void                 SetOpponents(SoccerTeam* opps)
        {
            m_pOpponents = opps;
        }

        team_color           Color()
        {
            return m_Color;
        }

        void                 SetPlayerClosestToBall(PlayerBase* plyr)
        {
            m_pPlayerClosestToBall=plyr;
        }
        PlayerBase*          PlayerClosestToBall()
        {
            return m_pPlayerClosestToBall;
        }

        double               ClosestDistToBallSq()
        {
            return m_dDistSqToBallOfClosestPlayer;
        }

        Ogre::Vector3             GetSupportSpot()
        {
            return m_pSupportSpotCalc->GetBestSupportingSpot();
        }

        PlayerBase*          SupportingPlayer()
        {
            return m_pSupportingPlayer;
        }
        void                 SetSupportingPlayer(PlayerBase* plyr)
        {
            m_pSupportingPlayer = plyr;
        }

        PlayerBase*          Receiver()
        {
            return m_pReceivingPlayer;
        }
        void                 SetReceiver(PlayerBase* plyr)
        {
            m_pReceivingPlayer = plyr;
        }

        PlayerBase*          ControllingPlayer()
        {
            return m_pControllingPlayer;
        }
        void                 SetControllingPlayer(PlayerBase* plyr)
        {
            m_pControllingPlayer = plyr;

            //rub it in the opponents faces!
            Opponents()->LostControl();
        }


        bool  InControl()
        {
            if (m_pControllingPlayer)return true;
            else return false;
        }
        void  LostControl()
        {
            m_pControllingPlayer = NULL;
        }

        PlayerBase*  GetPlayerFromID(int id);


        void SetPlayerHomeRegion(int plyr, int region);

        void DetermineBestSupportingPosition(){m_pSupportSpotCalc->DetermineBestSupportingPosition();}

        void UpdateTargetsOfWaitingPlayers();

        //returns false if any of the team are not located within their home region
        bool AllPlayersAtHome();

        std::string Name()
        {
            if (m_Color == blue) return "Blue";
            return "Red";
        }

    };
}
#endif
